

#pragma once

#include "select.h"

namespace Den{

struct SEmpty;


#define dHasSTLMethod(function_name)                             \
template<class T, bool VALID> struct _type_of_##function_name    \
{                                                                \
    typedef decltype(std::declval<T>().function_name())          \
        type;                                                    \
};                                                               \
template<class T> struct _type_of_##function_name<T, false>      \
{                                                                \
    typedef SEmpty                                               \
        type;                                                    \
};                                                               \
template<class T> class has_##function_name                      \
{                                                                \
    typedef char (&No)[1]; typedef char (&Yes)[2];               \
    template<class U>static Yes                                  \
        HelpMe( decltype(std::declval<U>().function_name())* p); \
    template<class U>static No HelpMe(...);                      \
public:                                                          \
    enum { value = sizeof(HelpMe<T>(0)) == sizeof(Yes) };        \
    typedef typename _type_of_##function_name<T, value>::type    \
        type;                                                    \
}

dHasSTLMethod(size);
dHasSTLMethod(begin);
dHasSTLMethod(end);

dHasSTLMethod(cbegin);
dHasSTLMethod(cend);

dHasSTLMethod(rbegin);
dHasSTLMethod(rend);

dHasSTLMethod(crbegin);
dHasSTLMethod(crend);

#define dHasSTLTypedef(typedef_name)                        \
template<class T, bool VALID>                               \
struct _get_type_##typedef_name                             \
{                                                           \
    typedef typename T::typedef_name                        \
        type;                                               \
};                                                          \
template<class T>                                           \
struct _get_type_##typedef_name<T, false>                   \
{                                                           \
    typedef SEmpty                                          \
        type;                                               \
};                                                          \
template<class T> struct has_##typedef_name                 \
{                                                           \
    typedef char (&No)[1]; typedef char (&Yes)[2];          \
    template<class U>static Yes                             \
        HelpMe( typename U::typedef_name* p);               \
    template<class U>static No HelpMe(...);                 \
    enum { value = sizeof(HelpMe<T>(0)) == sizeof(Yes) };   \
    typedef typename                                        \
        _get_type_##typedef_name<T, value>::type            \
            type;                                           \
}

dHasSTLTypedef(value_type);
dHasSTLTypedef(mapped_type);
dHasSTLTypedef(key_type);

dHasSTLTypedef(iterator);
dHasSTLTypedef(const_iterator);
dHasSTLTypedef(reverse_iterator);
dHasSTLTypedef(const_reverse_iterator);


template<class T> struct is_stl_compatible
{
    enum
    {
        eHAS_BEG            = has_begin<T>::value,
        eHAS_END            = has_end<T>::value,

        eHAS_CBEG           = has_cbegin<T>::value,
        eHAS_CEND           = has_cend<T>::value,

        eHAS_RBEG           = has_rbegin<T>::value,
        eHAS_REND           = has_rend<T>::value,

        eHAS_CRBEG          = has_crbegin<T>::value,
        eHAS_CREND          = has_crend<T>::value,

        eHAS_VALUE_TYPE     = has_value_type<T>::value,
        eHAS_MAPPED_TYPE    = has_mapped_type<T>::value,
        eHAS_KEY_TYPE       = has_key_type<T>::value,

        eHAS_ITERATOR       = has_iterator<T>::value,
        eHAS_CONST_ITERATOR = has_const_iterator<T>::value,

        eHAS_REVERSE_ITERATOR       = has_iterator<T>::value,
        eHAS_CONST_REVERSE_ITERATOR = has_const_iterator<T>::value,

        eIS_CONTAINER = eHAS_BEG && eHAS_END && eHAS_VALUE_TYPE
            && eHAS_ITERATOR && eHAS_CONST_ITERATOR,

        value = eIS_CONTAINER,

        eIS_ASSOCIATIVE = eIS_CONTAINER &&  eHAS_MAPPED_TYPE && eHAS_KEY_TYPE,
        eIS_LENEAR = eIS_CONTAINER      &&  !eIS_ASSOCIATIVE,
    };
};

}//namespace Den


